package com.cruisecontrol4eclipse.core.model;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import javax.management.InstanceNotFoundException;
import javax.management.MBeanServerConnection;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;

import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;

import com.cruisecontrol4eclipse.core.model.CruiseControlProject.Status;

public class CruiseControlProjectSyncronizer {

	private final MBeanServerConnection connection;
	private final ObjectName objectName;
	private final String host;
	private final int httpPort;
	private final boolean useDelayedUpdate;
	
	public CruiseControlProjectSyncronizer(CruiseControlProject project, MBeanServerConnection connection, ObjectName objectName, String host, int rmiPort, int httpPort) {
		this(project, connection, objectName, host, rmiPort, httpPort, true);
	}

	public CruiseControlProjectSyncronizer(final CruiseControlProject project, final MBeanServerConnection connection, final ObjectName objectName, String host, int rmiPort, int httpPort, final boolean useDelayedUpdate) {
		this.objectName = objectName;
		this.connection = connection;
		this.host = host;
		this.httpPort = httpPort;
		this.useDelayedUpdate = useDelayedUpdate;
		project.setName(objectName.getKeyProperty("name"));
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				//Cache values since we are not on the UI thread yet.
				final String lastSuccessfulBuildLabel = getLastSuccessfulBuildLabel();
				final Status status = Status.getStatusFromKey((String)get("Status"));
				final String label = (String) get("Label");
				PlatformUI.getWorkbench().getDisplay().asyncExec((new Runnable() {
					@Override
					public void run() {
						project.setLabel(label);
						project.setStatus(status);
						project.setLastSuccessfulBuildLabel(lastSuccessfulBuildLabel);
						try {
							connection.addNotificationListener(objectName, new CruiseNotificationListener(project, PlatformUI.getWorkbench().getDisplay()), null, null);
						} catch (InstanceNotFoundException e) {
							e.printStackTrace();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}));
			}
		}).start();
	}
	
	private Object get(String name) {
		try {
			return connection.getAttribute(objectName, name);
		} catch (Exception e) {
		}
		return null;
	}
	
	public String getLastSuccessfulBuildLabel(){
		String lastbuildTime = (String) get("LastSuccessfulBuild");
		URL url;
		try {
			url = new URL("http://"+host+":"+httpPort+"/buildresults/"+objectName.getKeyProperty("name"));
		
	        URLConnection urlConnection = url.openConnection();
	        BufferedReader in = new BufferedReader(
	                                new InputStreamReader(
	                                		urlConnection.getInputStream()));
	        String inputLine;
	
	        while ((inputLine = in.readLine()) != null) {
	        	if(inputLine.contains(lastbuildTime+"L")) {
	        		String buildLabel = inputLine.substring(inputLine.indexOf(lastbuildTime)+lastbuildTime.length()+1);
					if(buildLabel.indexOf("\"") == -1) {
						return "";
					}
	        		return buildLabel.substring(0, buildLabel.indexOf("\""));
	        	}
	        }
	        in.close();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	private class CruiseNotificationListener implements NotificationListener {
		private int delayCounter = 0;
		private final CruiseControlProject project;
		private final Display display;
		private String lastSuccessfulBuildLabel;
		private Status status;
		private String label;
		/**
		 * Listener is called on a separate thread then the UI thread.
		 * @param project
		 * @param display
		 */
		public CruiseNotificationListener(CruiseControlProject project, Display display) {
			this.project = project;
			this.display = display;
		}
		

		//can get event cruisecontrol.result.event CruiseControl Project
		//or can get event cruisecontrol.progress.event CruiseControl Project
		@Override
		public synchronized void handleNotification(final Notification notification, Object handback) {
			//Cache values since we are not on the UI thread yet.
			
			synchronized (display) {
				if("cruisecontrol.result.event".equals(notification.getType())){
					lastSuccessfulBuildLabel = getLastSuccessfulBuildLabel();
					label = (String) get("Label");	
				}
				status = Status.getStatusFromKey(notification.getUserData().toString());
			}
		
			if(useDelayedUpdate) {
				display.syncExec(new Runnable(){
					@Override
					public void run() {
						delayCounter++;
						display.timerExec(500, new Runnable() {
							public void run() {
								if (--delayCounter == 0) {
									synchronized (display) {
										project.setStatus(status);
										
										if(lastSuccessfulBuildLabel != null) {
											project.setLastSuccessfulBuildLabel(lastSuccessfulBuildLabel);
											project.setLabel(label);
											lastSuccessfulBuildLabel = null;
										}
									}
								}
							}
						});
					}
				});
			} else {
				display.syncExec(new Runnable(){
					@Override
					public void run() {
						project.setStatus(status);
						project.setLabel(label);
						project.setLastSuccessfulBuildLabel(lastSuccessfulBuildLabel);
					}
				});
			}
		}
	}
}
